package com.googlecode.gwtptk.client;

import java.io.Serializable;
import java.util.ArrayList;

/**
 * Wrapper for a list of results adding necessary page information. Object holds the list of results for
 * the page, offset of the first result from some datasource, total number of results, page size and possibly
 * message that can be used to communicate errors or "no results" or similar messages. List contains only
 * results for the page, not the whole result list.
 * <p/>
 * Object can be connected to PageControls, in that case it is affected by paging controls (for instance
 * {@link #firstResultOffset} is updated to a new position). Then it is expected that user somehow throw
 * this page of T away and asks for another one using the new state of this obsolete page - typically
 * in the implementation of the {@link PageControls#onChange()} method.
 *
 * @param <T> type of the item in the result
 */
public class PageOf<T> implements Serializable {
	/**
	 * "Page number" indicating that paging is not used at all.
	 */
	public static final int NO_PAGING = 0;

	/**
	 * First page - starting with 1 (from user perspective).
	 */
	public static final int FIRST_PAGE = 1;

	private ArrayList<T> list;
	private int firstResultOffset;
	private int pageSize;
	private int resultsCount;
	private String message;

	/**
	 * Default constructor.
	 */
	public PageOf() {
	}

	/**
	 * Constructor for server side creation - sets the list, offset and count information. Page size
	 * is either copied from request or filled according to settings on a server. Everything else
	 * can be computed from those three numbers.
	 *
	 * @param list list of items
	 * @param firstResultOffset first result offset
	 * @param pageSize page size in items
	 * @param resultsCount total count of items
	 */
	public PageOf(ArrayList<T> list, int firstResultOffset, int pageSize, int resultsCount) {
		this.list = list;
		this.firstResultOffset = firstResultOffset;
		this.pageSize = pageSize;
		this.resultsCount = resultsCount;
	}

	/**
	 * Used on a server side to inform front end about some error.
	 *
	 * @param message message - probably an error
	 */
	public PageOf(String message) {
		this.message = message;
	}

	/**
	 * Returns the list of results.
	 *
	 * @return list of results
	 */
	public ArrayList<T> getList() {
		return list;
	}

	/**
	 * Sets the list of results.
	 *
	 * @param list list of results
	 */
	public void setList(ArrayList<T> list) {
		this.list = list;
	}

	/**
	 * Adds pages - negative count can be used to go back. Current offset is
	 * verified and can not go out of limits.
	 *
	 * @param pages page addition
	 * @return offset of the new current page
	 */
	public int addPage(int pages) {
		firstResultOffset = firstResultOffset + pageSize * pages;
		fixFirstResult();
		return firstResultOffset;
	}

	private void fixFirstResult() {
		while (firstResultOffset > resultsCount) {
			firstResultOffset = firstResultOffset - pageSize;
		}
		if (firstResultOffset < 0) {
			firstResultOffset = 0;
		}
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getResultsCount() {
		return resultsCount;
	}

	public void setResultsCount(int resultsCount) {
		this.resultsCount = resultsCount;
	}

	public int currentPage() {
		return firstResultOffset / pageSize + 1;
	}

	public int totalPages() {
		return (resultsCount - 1) / pageSize + 1;
	}

	public int lastPageOffset() {
		return (totalPages() - 1) * pageSize;
	}

	public boolean hasPrevious() {
		return firstResultOffset > 0;
	}

	public boolean hasNext() {
		return firstResultOffset + pageSize < resultsCount;
	}

	public int getFirstResultOffset() {
		return firstResultOffset;
	}

	public void setFirstResultOffset(int firstResultOffset) {
		this.firstResultOffset = firstResultOffset;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public void goToFirstPage() {
		firstResultOffset = 0;
	}

	public void goToLastPage() {
		firstResultOffset = lastPageOffset();
	}
}
